#ifndef __MM_H__
#define __MM_H__

#include <platform.h>
#include <types.h>

#define PAGE_SIZE 4096
#define PAGE_OFFSET 12

/* memory layout */
extern uint8_t *_memory_start, *_memory_end;
extern uint8_t *_text_start, *_text_end;
extern uint8_t *_rodata_start, *_rodata_end;
extern uint8_t *_data_start, *_data_end;
extern uint8_t *_bss_start, *_bss_end;
extern uint8_t *_heap_start;
extern uint8_t *_heap_size;

#define PADDR(x) ((uintptr_t)&(x))
static uintptr_t MEMORY_START = PADDR(_memory_start);
static uintptr_t MEMORY_END = PADDR(_memory_end);
static uintptr_t TEXT_START = PADDR(_text_start);
static uintptr_t TEXT_END = PADDR(_text_end);
static uintptr_t RODATA_START = PADDR(_rodata_start);
static uintptr_t RODATA_END = PADDR(_rodata_end);
static uintptr_t DATA_START = PADDR(_data_start);
static uintptr_t DATA_END = PADDR(_data_end);
static uintptr_t BSS_START = PADDR(_bss_start);
static uintptr_t BSS_END = PADDR(_bss_end);
static uintptr_t HEAP_START = PADDR(_heap_start);
static uintptr_t HEAP_SIZE = PADDR(_heap_size);

static uintptr_t _alloc_start;
static uintptr_t _alloc_end;
static uint32_t _num_pages;

/* page flags */
#define PAGE_USED (uint8_t)(1 << 0)
#define PAGE_LAST (uint8_t)(1 << 1)

struct Page
{
    uint8_t flags;
};

static inline void _clear(struct Page *page) { page->flags = 0; }
static inline bool _is_free(struct Page *page) { return ((page->flags & PAGE_USED) == 0); }
static inline void _set_flag(struct Page *page, uint8_t flags) { page->flags |= flags; }
static inline bool _is_last(struct Page *page) { return ((page->flags & PAGE_LAST) != 0); }
static inline uintptr_t _align_page(uintptr_t addr)
{
    uint64_t order = (1 << PAGE_OFFSET) - 1;
    return (addr + order) & (~order);
}

void page_init(void);
void *page_alloc(int npages);
void page_free(void *p);

#endif /* !__MM_H__ */